package com.chuan.icons;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;

import org.json.JSONException;
import org.json.JSONObject;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.provider.Contacts.ContactMethods;
import android.provider.Contacts.People;

import com.chuan.icons.SiteBase.PairedType;

public final class Common {
	public static HashMap<Integer, JSONObject> mContactsDict = new HashMap<Integer, JSONObject>();
	public static ArrayList<String> mContactsName = new ArrayList<String>();
	public static HashMap<String, ArrayList<Integer>> mNameContactsDict = new HashMap<String, ArrayList<Integer>>();
	public static HashMap<Integer, Bitmap> mContactsPhoto = new HashMap<Integer, Bitmap>();

	// Levenshtein distance, refer: http://www.merriampark.com/ldjava.htm
	public static int getLevenshteinDistance(String s, String t) {
		if (s == null || t == null) {
			throw new IllegalArgumentException("Strings must not be null");
		}

		/*
		 * The difference between this impl. and the previous is that, rather
		 * than creating and retaining a matrix of size s.length()+1 by
		 * t.length()+1, we maintain two single-dimensional arrays of length
		 * s.length()+1. The first, d, is the 'current working' distance array
		 * that maintains the newest distance cost counts as we iterate through
		 * the characters of String s. Each time we increment the index of
		 * String t we are comparing, d is copied to p, the second int[]. Doing
		 * so allows us to retain the previous cost counts as required by the
		 * algorithm (taking the minimum of the cost count to the left, up one,
		 * and diagonally up and to the left of the current cost count being
		 * calculated). (Note that the arrays aren't really copied anymore, just
		 * switched...this is clearly much better than cloning an array or doing
		 * a System.arraycopy() each time through the outer loop.)
		 * 
		 * Effectively, the difference between the two implementations is this
		 * one does not cause an out of memory condition when calculating the LD
		 * over two very large strings.
		 */

		int n = s.length(); // length of s
		int m = t.length(); // length of t

		if (n == 0) {
			return m;
		} else if (m == 0) {
			return n;
		}

		int p[] = new int[n + 1]; // 'previous' cost array, horizontally
		int d[] = new int[n + 1]; // cost array, horizontally
		int _d[]; // placeholder to assist in swapping p and d

		// indexes into strings s and t
		int i; // iterates through s
		int j; // iterates through t

		char t_j; // jth character of t

		int cost; // cost

		for (i = 0; i <= n; i++) {
			p[i] = i;
		}

		for (j = 1; j <= m; j++) {
			t_j = t.charAt(j - 1);
			d[0] = j;

			for (i = 1; i <= n; i++) {
				cost = s.charAt(i - 1) == t_j ? 0 : 1;
				// minimum of cell to the left+1, to the top+1, diagonally left
				// and up +cost
				d[i] = Math.min(Math.min(d[i - 1] + 1, p[i] + 1), p[i - 1]
						+ cost);
			}

			// copy current distance counts to 'previous row' distance counts
			_d = p;
			p = d;
			d = _d;
		}

		// our last action in the above loop was to switch d and p, so p now
		// actually has the most recent cost counts
		return p[n];
	}

	public static boolean updateContacts(Context context) {
		String[] projection = new String[] { People._ID, People.NAME,
				People.NUMBER, People.PRIMARY_EMAIL_ID };

		ContentResolver cv = context.getContentResolver();
		Cursor peoplecursor = cv.query(People.CONTENT_URI, projection, null,
				null, People.DEFAULT_SORT_ORDER);

		if (!peoplecursor.moveToFirst()) {
			return false;
		}

		mContactsDict.clear();
		mNameContactsDict.clear();
		mContactsName.clear();

		Integer c_id;
		String mail_id, name, main_mail;
		do {
			c_id = peoplecursor.getInt(peoplecursor.getColumnIndex(People._ID));
			name = peoplecursor.getString(peoplecursor
					.getColumnIndex(People.NAME));
			mail_id = peoplecursor.getString((peoplecursor
					.getColumnIndex(People.PRIMARY_EMAIL_ID)));

			if (name == null || name.length() < 1) {
				continue;
			}

			JSONObject obj = new JSONObject();
			main_mail = "";
			if (mail_id != null && mail_id.length() >= 1) {
				Cursor mailcursor = cv
						.query(ContactMethods.CONTENT_EMAIL_URI,
								new String[] { ContactMethods.DATA },
								"contact_methods._id=?",
								new String[] { mail_id }, null);
				if (mailcursor.moveToFirst()) {
					main_mail = mailcursor.getString(mailcursor
							.getColumnIndex(ContactMethods.DATA));
				}
			}

			try {
				obj.put("c_id", c_id);
				obj.put("name", name);
				obj.put("email", main_mail);
				mContactsDict.put(c_id, obj);

				mContactsName.add(name);
				if (mNameContactsDict.get(name) == null) {
					ArrayList<Integer> t = new ArrayList<Integer>();
					t.add(c_id);
					mNameContactsDict.put(name, t);
				} else {
					mNameContactsDict.get(name).add(c_id);
				}
			} catch (JSONException e) {
				continue;
			}

		} while (peoplecursor.moveToNext());
		peoplecursor.close();

		return true;
	}

	public static ArrayList<Integer> pairContactByName(String name,
			PairedType type) {
		if (mNameContactsDict.get(name) != null) {
			type.type = Constants.PAIRED_TYPE;
			return mNameContactsDict.get(name);
		}

		int name_len = name.length();
		// TODO: try to pair the 2 name user.
		if (name_len <= 2) {
			type.type = Constants.UNKNOWN_TYPE;
			return null;
		}

		int min_len = -1;
		if (name_len == 3) {
			min_len = 1;
		} else {
			min_len = 2;
		}

		int t;
		ArrayList<Integer> id_list = null;
		for (int i = 0; i < mContactsName.size(); ++i) {
			t = getLevenshteinDistance(name, mContactsName.get(i));
			if (t < min_len) {
				t = min_len;
				id_list = mNameContactsDict.get(mContactsName.get(i));
			} else if (t == min_len) {
				ArrayList<Integer> temp_al = mNameContactsDict
						.get(mContactsName.get(i));
				for (int ti = 0; ti < temp_al.size(); ++ti) {
					if (id_list == null) {
						id_list = new ArrayList<Integer>();
					}
					id_list.add(temp_al.get(ti));
				}
			}
		}
		if (id_list == null) {
			type.type = Constants.UNKNOWN_TYPE;
		} else {
			type.type = Constants.GUESS_TYPE;
		}
		return id_list;
	}
	
	public static Boolean updateContactName(int personId, String name)
	{
		try {
			JSONObject obj = Common.mContactsDict.get(personId);
			if (obj != null) {
				obj.put("name", name);
				Common.mContactsDict.put(personId, obj);
				// TODO: Update table mContactsName
				return true;
			}
			return false;
		} catch (JSONException e1) {
		}
		return false;
	}

	public static String getContactNameByID(int personId) {
		try {
			JSONObject obj = Common.mContactsDict.get(personId);
			if (obj != null) {
				return obj.getString("name");
			}
			return null;
		} catch (JSONException e1) {
			return null;
		}
	}

	public static byte[] picDownload(String url) {
		URL pic_url;
		try {
			pic_url = new URL(url);
		} catch (MalformedURLException e1) {
			return null;
		}

		byte[] buf = null;
		HttpURLConnection http;
		InputStream in;
		try {
			http = (HttpURLConnection) pic_url.openConnection();
			http.setRequestMethod("GET");
			http.connect();
			in = http.getInputStream();
			int size = http.getContentLength();
			if (size <= 0) {
				return null;
			}
			buf = new byte[size];

			if ((in.read(buf)) == -1) {
				return null;
			}

			in.close();
		} catch (IOException e) {
			return null;
		}

		return buf;
	}
}
